#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
认证路由

这个模块包含用户认证相关的 API 端点：
1. 用户注册
2. 用户登录
3. 令牌刷新
4. 密码重置
5. 用户注销

使用 JWT 令牌进行身份验证
支持 OAuth2 密码流

作者: FastAPI 学习项目
创建时间: 2024
"""

from datetime import timedelta
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, status, Body
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from pydantic import BaseModel, EmailStr
from loguru import logger

from app.database.connection import get_db
from app.database.crud import crud_user
from app.models.user import User, UserCreate, UserResponse, UserLogin, UserPasswordChange
from app.utils.security import (
    create_token_response,
    refresh_access_token,
    get_current_active_user,
    generate_password_reset_token,
    verify_password_reset_token,
    get_password_hash
)
from config import settings

# 创建路由器
router = APIRouter()


# Pydantic 模型
class TokenResponse(BaseModel):
    """
    令牌响应模型
    """
    access_token: str
    refresh_token: str
    token_type: str
    expires_in: int
    user: dict


class RefreshTokenRequest(BaseModel):
    """
    刷新令牌请求模型
    """
    refresh_token: str


class RefreshTokenResponse(BaseModel):
    """
    刷新令牌响应模型
    """
    access_token: str
    token_type: str
    expires_in: int


class PasswordResetRequest(BaseModel):
    """
    密码重置请求模型
    """
    email: EmailStr


class PasswordResetConfirm(BaseModel):
    """
    密码重置确认模型
    """
    token: str
    new_password: str


class MessageResponse(BaseModel):
    """
    消息响应模型
    """
    message: str


@router.post(
    "/register",
    response_model=UserResponse,
    status_code=status.HTTP_201_CREATED,
    summary="用户注册",
    description="创建新用户账户"
)
async def register(
    user_in: UserCreate,
    db: Session = Depends(get_db)
) -> Any:
    """
    用户注册
    
    创建新的用户账户。需要提供用户名、邮箱和密码。
    
    Args:
        user_in: 用户注册信息
        db: 数据库会话
        
    Returns:
        UserResponse: 创建的用户信息
        
    Raises:
        HTTPException: 当用户名或邮箱已存在时抛出 400 错误
        
    Example:
        ```json
        {
            "username": "newuser",
            "email": "newuser@example.com",
            "password": "SecurePass123",
            "confirm_password": "SecurePass123",
            "full_name": "New User"
        }
        ```
    """
    try:
        logger.info(f"用户注册请求: {user_in.username} ({user_in.email})")
        
        # 检查用户名是否已存在
        existing_user = crud_user.get_by_username(db, username=user_in.username)
        if existing_user:
            logger.warning(f"用户名已存在: {user_in.username}")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="用户名已存在"
            )
        
        # 检查邮箱是否已存在
        existing_user = crud_user.get_by_email(db, email=user_in.email)
        if existing_user:
            logger.warning(f"邮箱已存在: {user_in.email}")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="邮箱地址已存在"
            )
        
        # 创建用户
        user = crud_user.create(db, obj_in=user_in)
        logger.info(f"用户注册成功: {user.username} (ID: {user.id})")
        
        return user
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"用户注册失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="注册失败，请稍后重试"
        )


@router.post(
    "/login",
    response_model=TokenResponse,
    summary="用户登录",
    description="用户登录并获取访问令牌"
)
async def login(
    form_data: OAuth2PasswordRequestForm = Depends(),
    db: Session = Depends(get_db)
) -> Any:
    """
    用户登录
    
    使用用户名/邮箱和密码进行登录，返回访问令牌和刷新令牌。
    
    Args:
        form_data: OAuth2 密码表单数据
        db: 数据库会话
        
    Returns:
        TokenResponse: 包含访问令牌和用户信息的响应
        
    Raises:
        HTTPException: 当认证失败时抛出 401 错误
    """
    try:
        logger.info(f"用户登录请求: {form_data.username}")
        
        # 验证用户凭据
        user = crud_user.authenticate(
            db, 
            username=form_data.username, 
            password=form_data.password
        )
        
        if not user:
            logger.warning(f"登录失败: {form_data.username}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误",
                headers={"WWW-Authenticate": "Bearer"},
            )
        
        # 创建令牌响应
        token_data = create_token_response(user)
        logger.info(f"用户登录成功: {user.username} (ID: {user.id})")
        
        return token_data
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"用户登录异常: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="登录失败，请稍后重试"
        )


@router.post(
    "/refresh",
    response_model=RefreshTokenResponse,
    summary="刷新访问令牌",
    description="使用刷新令牌获取新的访问令牌"
)
async def refresh_token(
    token_request: RefreshTokenRequest,
    db: Session = Depends(get_db)
) -> Any:
    """
    刷新访问令牌
    
    使用有效的刷新令牌获取新的访问令牌。
    
    Args:
        token_request: 包含刷新令牌的请求
        db: 数据库会话
        
    Returns:
        RefreshTokenResponse: 新的访问令牌
        
    Raises:
        HTTPException: 当刷新令牌无效时抛出 401 错误
        
    Example:
        ```json
        {
            "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
        }
        ```
    """
    try:
        logger.info("刷新令牌请求")
        
        # 刷新访问令牌
        token_data = refresh_access_token(token_request.refresh_token, db)
        logger.info("令牌刷新成功")
        
        return token_data
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"令牌刷新异常: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="令牌刷新失败，请稍后重试"
        )


@router.post(
    "/logout",
    response_model=MessageResponse,
    summary="用户注销",
    description="注销当前用户（客户端应删除令牌）"
)
async def logout(
    current_user: User = Depends(get_current_active_user)
) -> Any:
    """
    用户注销
    
    注销当前用户。由于使用 JWT 令牌，服务端无法直接使令牌失效，
    客户端应该删除存储的令牌。
    
    Args:
        current_user: 当前登录用户
        
    Returns:
        MessageResponse: 注销成功消息
    """
    try:
        logger.info(f"用户注销: {current_user.username} (ID: {current_user.id})")
        
        return {"message": "注销成功"}
        
    except Exception as e:
        logger.error(f"用户注销异常: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="注销失败，请稍后重试"
        )


@router.post(
    "/password-reset-request",
    response_model=MessageResponse,
    summary="请求密码重置",
    description="发送密码重置邮件（演示版本仅返回重置令牌）"
)
async def password_reset_request(
    request: PasswordResetRequest,
    db: Session = Depends(get_db)
) -> Any:
    """
    请求密码重置
    
    根据邮箱地址生成密码重置令牌。在实际应用中，
    这个令牌应该通过邮件发送给用户。
    
    Args:
        request: 密码重置请求
        db: 数据库会话
        
    Returns:
        MessageResponse: 包含重置令牌的消息（仅用于演示）
        
    Note:
        在生产环境中，不应该在响应中返回重置令牌，
        而应该通过邮件发送给用户。
    """
    try:
        logger.info(f"密码重置请求: {request.email}")
        
        # 查找用户
        user = crud_user.get_by_email(db, email=request.email)
        if not user:
            # 为了安全，即使用户不存在也返回成功消息
            logger.warning(f"密码重置请求的邮箱不存在: {request.email}")
            return {"message": "如果邮箱存在，重置链接已发送"}
        
        if not user.is_active:
            logger.warning(f"密码重置请求的用户未激活: {request.email}")
            return {"message": "如果邮箱存在，重置链接已发送"}
        
        # 生成重置令牌
        reset_token = generate_password_reset_token(user.id)
        
        # 在实际应用中，这里应该发送邮件
        # send_password_reset_email(user.email, reset_token)
        
        logger.info(f"密码重置令牌已生成: {user.username}")
        
        # 演示版本：在响应中返回令牌（生产环境中不要这样做！）
        return {
            "message": f"密码重置令牌（仅用于演示）: {reset_token}"
        }
        
    except Exception as e:
        logger.error(f"密码重置请求异常: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="密码重置请求失败，请稍后重试"
        )


@router.post(
    "/password-reset-confirm",
    response_model=MessageResponse,
    summary="确认密码重置",
    description="使用重置令牌设置新密码"
)
async def password_reset_confirm(
    request: PasswordResetConfirm,
    db: Session = Depends(get_db)
) -> Any:
    """
    确认密码重置
    
    使用有效的重置令牌设置新密码。
    
    Args:
        request: 密码重置确认请求
        db: 数据库会话
        
    Returns:
        MessageResponse: 密码重置成功消息
        
    Raises:
        HTTPException: 当重置令牌无效时抛出 400 错误
        
    Example:
        ```json
        {
            "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
            "new_password": "NewSecurePass123"
        }
        ```
    """
    try:
        logger.info("密码重置确认请求")
        
        # 验证重置令牌
        user_id = verify_password_reset_token(request.token)
        if not user_id:
            logger.warning("无效的密码重置令牌")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="无效或已过期的重置令牌"
            )
        
        # 获取用户
        user = crud_user.get(db, id=user_id)
        if not user:
            logger.warning(f"密码重置的用户不存在: {user_id}")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="用户不存在"
            )
        
        if not user.is_active:
            logger.warning(f"密码重置的用户未激活: {user.username}")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="用户账户未激活"
            )
        
        # 更新密码
        crud_user.update_password(db, user=user, new_password=request.new_password)
        logger.info(f"密码重置成功: {user.username}")
        
        return {"message": "密码重置成功"}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"密码重置确认异常: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="密码重置失败，请稍后重试"
        )


@router.post(
    "/change-password",
    response_model=MessageResponse,
    summary="修改密码",
    description="修改当前用户的密码"
)
async def change_password(
    password_data: UserPasswordChange,
    current_user: User = Depends(get_current_active_user),
    db: Session = Depends(get_db)
) -> Any:
    """
    修改密码
    
    允许已登录用户修改自己的密码。
    
    Args:
        password_data: 密码修改数据
        current_user: 当前登录用户
        db: 数据库会话
        
    Returns:
        MessageResponse: 密码修改成功消息
        
    Raises:
        HTTPException: 当当前密码错误时抛出 400 错误
        
    Example:
        ```json
        {
            "current_password": "OldPassword123",
            "new_password": "NewPassword123",
            "confirm_new_password": "NewPassword123"
        }
        ```
    """
    try:
        logger.info(f"密码修改请求: {current_user.username}")
        
        # 验证当前密码
        from app.utils.security import verify_password
        if not verify_password(password_data.current_password, current_user.hashed_password):
            logger.warning(f"当前密码错误: {current_user.username}")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="当前密码错误"
            )
        
        # 更新密码
        crud_user.update_password(
            db, 
            user=current_user, 
            new_password=password_data.new_password
        )
        
        logger.info(f"密码修改成功: {current_user.username}")
        
        return {"message": "密码修改成功"}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"密码修改异常: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="密码修改失败，请稍后重试"
        )


@router.get(
    "/me",
    response_model=UserResponse,
    summary="获取当前用户信息",
    description="获取当前登录用户的详细信息"
)
async def get_current_user_info(
    current_user: User = Depends(get_current_active_user)
) -> Any:
    """
    获取当前用户信息
    
    返回当前登录用户的详细信息。
    
    Args:
        current_user: 当前登录用户
        
    Returns:
        UserResponse: 用户详细信息
    """
    try:
        logger.info(f"获取用户信息: {current_user.username}")
        return current_user
        
    except Exception as e:
        logger.error(f"获取用户信息异常: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="获取用户信息失败"
        )


@router.get(
    "/verify-token",
    response_model=dict,
    summary="验证令牌",
    description="验证访问令牌是否有效"
)
async def verify_access_token(
    current_user: User = Depends(get_current_active_user)
) -> Any:
    """
    验证令牌
    
    验证当前访问令牌是否有效。
    
    Args:
        current_user: 当前登录用户
        
    Returns:
        dict: 令牌验证结果
    """
    try:
        return {
            "valid": True,
            "user_id": current_user.id,
            "username": current_user.username,
            "message": "令牌有效"
        }
        
    except Exception as e:
        logger.error(f"令牌验证异常: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="令牌验证失败"
        )